// 本节主要讲的是rust中的模式
// 模式有match, 也有if let
#[warn(dead_code)]
fn main() {
    // 1. if let案例
    let like_color: Option<&str> = None;
    let is_tuesday = false;
    // 这里本质上是尝试将一个字符串类型"34"转为数字u8类型
    // parse返回的是一个Result类型, 它有俩变体: Ok和Err
    let age:Result<u8, _> = "34".parse();
    // 其实我的理解是, if let 后面跟着的就是一个新声明的变量, 就看前面的Result结果
    // 能被赋给哪种变量, 就走哪个分支(这部分是我自己练习用的, 非视频主要内容)
    // if let Ok(age) = age {
    //     println!("age成功被转换为了数字: {}", age);
    // } else if let Err(wrong) = age {
    //     println!("age转换失败了: {}", wrong);
    // }

    // 以下部分为视频内容
    if let Some(color) = like_color {
        println!("颜色是: {}", color);
    } else if is_tuesday {
        println!("今天是星期二")
    } else if let Ok(age) = age {
        println!("年龄是: {}", age)
    }

    // 2.while let
    let mut stack = Vec::new();
    stack.push(1);
    stack.push(2);
    stack.push(3);
    while let Some(top) = stack.pop() {
        println!("top is {}", top); // 3, 2, 1
    }

    // 3. for循环, for后面的就是要匹配的模式
    let v = vec!['a', 'b', 'c'];
    // enumerate()是一个迭代器适配器，它接受一个迭代器作为输入，并产生一个包含元组（tuple）的新迭代器，
    // 每个元组包含原始迭代器的当前索引和值。这对于需要同时访问索引和值的场景非常有用。
    for (index, value) in v.iter().enumerate() {
        println!("index is {}, value is {}", index, value)
    }
    // 4. let也是模式
    let (a,b,c) = (1,2,3);
    println!("a:{},b:{},c:{}", a, b, c);
    // 注意数量要对应才行, 如以下, 数量不匹配, 就会报错
    // let (d,e) = (4,5,6);
    // 5. 函数参数
    // 假如一个函数的参数是一个元组的引用类型
    let point = (1,2);
    fn print_coordinates(&(x,y): &(i32, i32)) {
        println!("The x: {}, The y: {}", x, y);
    }
    print_coordinates(&point);
    // 6. match
    let name: Result<i32, _> = "asdflkj".parse();
    match name {
        Ok(name) => println!("this is string: {}", name),
        Err(e) => println!("The error is: {}", e)
    }

    // 18.2内容, 可辩驳即: 能够失败; 不可辩驳: 不会失败
    // 其中: let语句, for循环, 函数参数, 都只能接受无可辩驳的模式
    // if let, while let, 能够接受可辩驳和不可辩驳的模式
    // 例如:
    let a:Option<i32> = Some(5); // let接受不可辩驳的模式, 所以是正确的, Some无论如何都可以赋值给Option
    // let Some(b) = a; // let 后只能跟不可辩驳模式, 所以, 此处错误
    // 可以使用if let ; 说人话就是let是正儿八经的赋值, 而if let则本就允许不匹配
    if let Some(b) = a {
        println!("the b is {}", b)
    };
    // 同样if let 后面不能跟着不可辩驳的模式
    // if let c = 5 { // 这里不会报错, 但会有警告
    //     println!("c is {}", c);
    // }

    // 18.3内容
    // 1. 字面量直接匹配
    #[warn(unused_assignments)]
    let x = 1;
    match x {
        1 => println!("this is one: {}", x),
        2 => println!("this is two: {}", x),
        _ => println!("any {}", x)
    }
    println!("so the x is {}", x);
    // 2. 命名的变量可以匹配任何值的无可辩驳模式
    // 我理解的是match中的y可以匹配处于无可辩驳模式的x
    let x = Some(50);
    let y = 1;
    match x {
        Some(5) => println!("this is five"),
        Some(y) => println!("The x is {}", y), // 此处的外不是外面的y
        _ => println!("x is None")
    }
    println!("x is {:?}, y is {}", x, y); // 注意, 这里的y不是match中的y
    // 这里我个人引申一下如何将Some中的值取出, 出了上面的利用Some(y)这种, 还有unwrap, unwrap_or, unwrap_or_else
    let bb = None;
    // 如果bb是None, unwrap就会直接panic
    // let bb_value = bb.unwrap();
    // println!("bb_value is {}", bb_value);
    // unwrap_or则可以设置默认值
    let bb_or_value = bb.unwrap_or(0);
    println!("bb_or_value is {}", bb_or_value);
    // 还可以使用unwrap_or_else来加入些复杂判断
    let bb_else_value = bb.unwrap_or_else(|| {
        println!("你这瓜保熟吗?");
        0
    });
    println!("bb_else_value: {}", bb_else_value);
    // 3. 多重模式
    let mm = 12;
    match mm {
        1 | 2 => println!("mm的结果是1或者2"),
        _ => println!("there no anything")
    };

    // 4. 范围
    // 数字
    let range_value = 1;
    match range_value {
        1..=2 => println!("数据的值在1-2之间"),
        3..=5 => println!("数据的值在3-5之间"),
        _ => println!("啥也不是")
    }
    // 字母
    let range_letter = 'c';
    match range_letter {
        'a' ..= 'c' => println!("数据在a-c之间"),
        'd' ..= 'f' => println!("数据在d-f之间"),
        _ => println!("range_letter 什么也不是")
    }
    // 5. 利用模式来解构struct, enum, tupe
    // 5-1: 解构struct
    struct Point {
        x: i32,
        y: i32
    }
    let p_value = Point {x: 0, y: 2};
    let Point {x: a, y: b} = p_value;
    println!("a is {}, b is {}", a, b);
    // 当然, 我们还可以不去修改结构体内的属性名
    let Point {x, y} = p_value;
    println!("x is {}, y is {}", x, y);
    // 甚至还可以使用match来做解构匹配
    match p_value {
        Point {x: 0, y} => println!("x 固定为0, y: {}", y),
        Point {x, y: 0} => println!("x 为 {}, y 固定为0", x),
        Point {x, y} => println!("x: {}, y: {}", x, y),
    }

    // 5-2: 解构枚举
    // 以下存在大量未使用的代码, 开启警告(其实默认已经开启), 用#[warn(dead_code)]
    enum Message {
        Quit,
        Move{x: i32, y: i32},
        Write(String),
        ChangeColor(i32, i32, i32)
    }
    let message_value = Message::Move { x: 1, y: 2 };
    match message_value {
        Message::Quit => println!("this is quit"),
        Message::ChangeColor(r, g,b ) => println!("The color is r: {}, g:{}, b:{}", r, g, b),
        Message::Write(str) => println!("write is {}", str),
        Message::Move { x, y } => println!("point x is {}, y is {}", x, y),
    }

    // 5-3嵌套枚举
    enum Color {
      Rgb(i32, i32, i32),
      Hsv(i32, i32, i32),
      Write(String) // 为了防止最后一个_ => 变为永不可到达的分支, 这里强行再加个变体
    }
    enum Message2 {
        ChangeColor(Color)
    }

    let message_value2 = Message2::ChangeColor(Color::Rgb(233, 231, 199));
    // 哪怕是嵌套的, 也可以解构出来
    match message_value2 {
        Message2::ChangeColor(Color::Hsv(r, g,b ))
            => {println!("Hsv r:{}, g:{}, b:{}", r, g, b)}
        Message2::ChangeColor(Color::Rgb(r,g ,b ))
            => {println!("Rgb r:{}, g:{}, b:{}", r, g, b)} // unreachable pattern表示用不可能到达的分支
        _ => {println!("Message2 no any match")}
    }

    // 5-4 解构元组和结构体
    struct PP {
        x: i32,
        y: i32
    }

    let tupe_value = (1,2);
    // 解构
    let (PP{x, y}, (a, b)) = (PP {x: 12, y: 12}, tupe_value);
    println!("tupe_value x: {} y: {} a: {} b: {}", x, y, a, b);
    // 5-5 使用_忽略
    fn ignore_fn(_:i32, y: i32) {
        println!("ignore_fn y is: {}", y);
    }
    ignore_fn(1,2);

    // 5-6忽略某一部分
    let mut setting_value = Some(5);
    let setting_value_new = Some(10);
    match (setting_value, setting_value_new) {
        (Some(_), Some(_)) => println!("The two value is Some"),
        _ => {
            println!("no there no some");
            setting_value = setting_value_new
        }
    }
    println!("setting_value {:?}, setting_value_new {:?}", setting_value, setting_value_new);
    // 5-7 忽略某一部分2
    let tupe_value2 = (1,2,3,4,5);
    match tupe_value2 {
        // 我只要1,3,4, 不要2,4, 就可以使用_来忽略
        (one, _, three, _, five) => {
            println!("one {}, three {}, five {}", one, three, five)
        }
    }
    // 5-8 忽略未使用的变量, 开头用_
    let _unused_value = 5; // 即使后续我没有使用这个变量, 也不会发出警告
    // Some解构中也可以使用
    let s = Some(String::from("string_value"));
    if let Some(_) = s { // 这里使用_, 就不会发生借用!
        // 这里哪怕没有用_str, 也没有关系
        println!("_str is ??");
    }
    // 这里注意了, 如果上面的if let Some(_) = s {...}
    // 则此处不会报错
    println!("The s is {:?}", s);

    // 5-9 忽略解构的部分, 使用..
    struct Point2 {
        x: i32,
        _y: i32,
        _z: i32
    }

    let Point2 {x: x2, ..} = Point2 {x: 1, _y: 2, _z: 3};
    println!("Point2.x is {}", x2);
    let numbers = (1,2,3,4,5);
    match numbers {
        // 不过要注意了, .. 不能放在两侧边缘, 例如: (.., three, ..)
        (one, .., five) => {
            println!("numbers one is {}, five is {}", one, five)
        }
    }
    // 6. match守卫
    // 就是在match基础上再加上if判断
    let num = Some(4);
    match num {
        // 注意只有模式才会引入新的变量,
        // 比如这里的Some(v), 中的v, 后面的if不会引入新变量
        Some(v) if v < 3 => println!("yes is right"),
        Some(v) => println!("the v is {}", v),
        None => ()
    }
    let num_value = 5;
    let num_bool = false;
    match num_value {
        // 当值为4或者5或者6, 且num_bool为true
        4|5|6 if num_bool => {
            println!("the num_value is {}", num_value)
        },
        _ => ()
    }

    // 7.绑定
    enum Message3 {
        Hello{id:i32}
    }
    let binding_value = Message3::Hello { id: 5 };
    match binding_value {
        Message3::Hello {
            // 匹配到的同时, 将值绑定到temp变量上
            id: temp @ 1..=5
        } => {
            println!("the temp value is {}", temp);
        },
        _ => ()
    }
}
